import { VNode } from "./vnode.js"

/**
 * 在Vue实例上安装运行时的渲染帮助函数，比如 _c,_v, 这些函数会生成VNode
 * @param {*} target vue 实例
 */
export function renderHelper(target) {
  target._c = createElement
  target._v = createTextNode
  target._t = renderSlot
}
/**
 * 根据标签信息创建VNode
 * @param {*} tag 标签名
 * @param {*} attr 标签属性的map对象
 * @param {*} children 所有子元素的渲染函数
 * @returns 
 */
function createElement(tag, attr, children) {
  return VNode(tag, attr, children, this)
}
/**
 * 生成文本节点的VNode
 * @param {*} textAst 文本节点的ast对象
 */
function createTextNode(textAst) {
  return VNode(null, null, null, this, textAst)
}

/**
 * 插槽的原理是： 生成VNode
 * @param {*} attrs 插槽属性
 * @param {*} children 插槽所有子节点的AST组成的数组
 */
function renderSlot(attrs, children) {
  // 父组件VNode的attr信息
  const parentAttr = this._parentVnode.attr
  let vnode = null
  if(parentAttr.scopedSlot) {
    // 说明给当前组件的插槽传递了内容
    const slotName = attrs.name
    const slotInfo = parentAttr.scopedSlot[slotName]

    this[slotInfo.scopedSlot] = this[Object.keys(attrs.vBind)[0]]
    vnode = genVNode(slotInfo.children, this)
  } else {
    vnode = genVNode(children, this)
  }
  // 如果只有一个子节点
  if(children.length === 1) return vnode[0]
  return createElement.call(this, 'div', {}, vnode)
}

/**
 * 将一批ast节点转成vnode数组
 * @param {*} childs 节点数组
 * @param {*} vm 组件实例
 */
function genVNode(childs, vm) {
  const vnode = []
  for(let i = 0; i < childs.length; i++) {
    const { tag, attr, children, text } = childs[i]
    if(text) {
      // 构造文本节点的AST对象
      const textAst = {
        type: 3,
        text
      }
      // 这个text是一个ast对象来着
      if(text.text.match(/{{(.*)}}/)) {
        // 表达式 expression为匹配到的内容
        textAst.expression = RegExp.$1.trim()
      }
      vnode.push(createTextNode.call(vm, textAst))
    } else {
      // 元素节点
      vnode.push(createElement.call(vm, tag, attr, genVNode(children, vm)))
    }
  }
  return vnode
}